package com.lipaluma.field.mapper;

import java.lang.reflect.Field;

import com.lipaluma.field.merge.FieldMergedFunction;
import com.lipaluma.util.ReflectionUtils;

/**
 * Abstract MapColumn who defines the basic algorithm of the parsing for a column :
 * <ul> 
 * <li>We extract the value of the row</li>
 * <li>We adapt the value with transformation like trim, ...</li>
 * <li>When the value is adapted, we parse the value to the type of the field</li>
 * <li>We validate the value using validators defined by the user</li>
 * <li>we finish by injecting the value on the instance of the POJO</li>
 * </ul>
 * @author Mario
 *
 */
public abstract class AbstractFieldMapper implements FieldMapper, FieldIdAssignable, FieldMergeable {
	protected Field field;
	private boolean fieldAsId;	
	private FieldMergedFunction fieldMergedFunction;
	private boolean oneToMany;
	
	/**
	 * inject the value parsed on the target entity instance
	 * 
	 * @param value value parsed
	 * @param target instance of the target entity
	 */
	
	public void injectValueToObject(Object value, Object target) {
		ReflectionUtils.makeAccessible(field);
		ReflectionUtils.setField(field, target, value);
	}
	
//	public void unmarshall(Row row, Object target) {
//		String value = extractValue(row);
//		Object valueParsed = null;
//		if(value != null) {
//			if(adaptor != null)
//				value = adaptor.adapt(value);
//			try {
//				valueParsed = parse(value);
//			} catch (Exception e) {
//				throw new CsvParserProcessException(getColumnDescription()+".\n Unparseable value "+value, e);
//			}
//		}
//		if(validator != null) {
//			List<ValidationError> errors = new ArrayList<ValidationError>();
//			if(!validator.validate(valueParsed, errors)) {
//				StringBuilder message = new StringBuilder(getColumnDescription());
//				message.append("An error occured on validation of the value ").append(value).append("\n");
//				for (ValidationError error : errors) {
//					message.append(error.getMessage()).append("\n");
//				}
//				throw new CsvParserValidationException(message.toString());
//			}
//		}
//		injectValue(valueParsed, target);
//	}

//	public void marshal(Object from, Row row) {
//		Object value = extractValueFromObject(from);
//		writeValueOnRow(value, row);
//	}

	/**
	 * Extract the value from the object
	 * @param from object
	 * @return the value;
	 */
	public Object extractValueFromObject(Object from) {
		if(from == null)
			return null;
		ReflectionUtils.makeAccessible(field);
		return ReflectionUtils.getField(field, from);
	}
	
	
//	public void marshal(Object from, Row row) {
//		Object value = extractValue(from);
//		if(validator != null) {
//			List<ValidationError> errors = new ArrayList<ValidationError>();
//			if(!validator.validate(value, errors)) {
//				StringBuilder message = new StringBuilder(getColumnDescription());
//				message.append("An error occured on validation of the value ").append(value).append("\n");
//				for (ValidationError error : errors) {
//					message.append(error.getMessage()).append("\n");
//				}
//				throw new CsvParserValidationException(message.toString());
//			}
//		}
//		if(value == null)
//			insertNullValueOnRow(row);
//		else {
//			Object valueFormatted = formatValue(value);
//			writeValueOnRow(valueFormatted, row);
//		}
//	}

//	/**
//	 * Parse the value adapted on the the type of the field
//	 * 
//	 * @param value value extracted already adapted
//	 * @return the Object parsed to the type of the field
//	 */
//	protected abstract Object parse(String value);
	
	@Override	
	public Field getField() {
		return field;
	}
	
//	@Override
//	public void setField(Field field) {
//		this.field = field;
//	}

//	public void setField(Field field) {
//		this.field = field;
//	}
	
	public String generateKey(Object target) {
		return String.valueOf(extractValueFromObject(target));
	}
	
	@Override
	public void assignId(boolean asId) {
		this.fieldAsId = asId;
	}

	@Override
	public boolean isIdAssigned() {
		return fieldAsId;
	}
	
	@Override
	public boolean hasToBeMerged() {
		return fieldMergedFunction != null;
	}
	
	@Override
	public FieldMergedFunction mergedBy() {
		return fieldMergedFunction;
	}
	
	@Override
	public void mergeWith(FieldMergedFunction fieldMergedFunction) {
		this.fieldMergedFunction = fieldMergedFunction;
	}
	
	@Override
	public String toString() {
		return getClass().toString();
	}
}
